package com.shlh.saas.exception;

import com.shlh.saas.common.Result;
import org.apache.catalina.connector.ClientAbortException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.sql.SQLException;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 全局异常处理器
 * 捕获所有异常并返回友好的错误信息
 */
@RestControllerAdvice
public class GlobalExceptionHandler {
    
    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    /**
     * 处理业务异常
     */
    @ExceptionHandler(BusinessException.class)
    @ResponseStatus(HttpStatus.OK)
    public Result handleBusinessException(BusinessException e) {
        logger.warn("业务异常: {}", e.getMessage());
        return Result.error(e.getMessage());
    }

    /**
     * 处理参数校验异常 (JSR303)
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.OK)
    public Result handleMethodArgumentNotValid(MethodArgumentNotValidException e) {
        BindingResult bindingResult = e.getBindingResult();
        List<FieldError> fieldErrors = bindingResult.getFieldErrors();
        String errorMessage = fieldErrors.stream()
                .map(FieldError::getDefaultMessage)
                .collect(Collectors.joining(", "));
        
        logger.warn("参数校验失败: {}", errorMessage);
        return Result.error(errorMessage);
    }

    /**
     * 处理普通参数绑定异常
     */
    @ExceptionHandler(BindException.class)
    @ResponseStatus(HttpStatus.OK)
    public Result handleBindException(BindException e) {
        BindingResult bindingResult = e.getBindingResult();
        List<FieldError> fieldErrors = bindingResult.getFieldErrors();
        String errorMessage = fieldErrors.stream()
                .map(FieldError::getDefaultMessage)
                .collect(Collectors.joining(", "));
        
        logger.warn("参数绑定失败: {}", errorMessage);
        return Result.error(errorMessage);
    }

    /**
     * 处理请求参数缺失异常
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    @ResponseStatus(HttpStatus.OK)
    public Result handleMissingServletRequestParameter(MissingServletRequestParameterException e) {
        logger.warn("请求参数缺失: {}", e.getMessage());
        return Result.error("请求参数'" + e.getParameterName() + "'不能为空");
    }

    /**
     * 处理SQL异常
     */
    @ExceptionHandler(SQLException.class)
    @ResponseStatus(HttpStatus.OK)
    public Result handleSQLException(SQLException e) {
        logger.error("数据库异常: ", e);
        
        // 分析SQL异常，返回友好的错误消息
        String message = "数据库操作失败";
        if (e.getMessage().contains("Duplicate entry")) {
            message = "数据已存在，请勿重复提交";
        } else if (e.getMessage().contains("foreign key constraint fails")) {
            message = "操作失败，数据被其他记录引用";
        }
        
        return Result.error(message);
    }

    /**
     * 处理客户端中止连接异常（通常是前端超时主动断开）
     * 这是正常现象，不应该打印ERROR级别日志
     */
    @ExceptionHandler(ClientAbortException.class)
    public void handleClientAbortException(ClientAbortException e) {
        // 客户端主动断开连接（通常是超时），只记录INFO级别日志
        logger.info("客户端中止连接（可能是请求超时）: {}", e.getMessage());
        // 不返回任何内容，因为连接已断开
    }

    /**
     * 处理所有运行时异常
     */
    @ExceptionHandler(RuntimeException.class)
    @ResponseStatus(HttpStatus.OK)
    public Result handleRuntimeException(RuntimeException e) {
        logger.error("运行时异常: ", e);
        return Result.error(e.getMessage());
    }

    /**
     * 处理所有异常
     */
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.OK)
    public Result handleException(Exception e) {
        // 检查是否是ClientAbortException（包装在其他异常中）
        if (e.getCause() instanceof ClientAbortException || 
            e.getMessage() != null && e.getMessage().contains("你的主机中的软件中止了一个已建立的连接")) {
            logger.info("客户端中止连接（可能是请求超时）");
            return null;
        }
        
        logger.error("系统异常: ", e);
        return Result.error("系统异常，请联系管理员");
    }
} 